package net.wicp.tams.commons.apiext;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import net.wicp.tams.commons.io.UnsignedLong;

import org.apache.commons.lang3.ArrayUtils;

/**
 * 
 * @author 偏锋书生
 *
 */
public abstract class ByteUtil {
	/***
	 * 小字节序转为大字节序
	 * 
	 * @param value
	 *            小端字节
	 * @return 大端字节
	 */
	public static byte[] toBigEndian(byte[] value) {
		for (int i = 0, length = value.length >> 2; i <= length; i++) {
			final int j = value.length - 1 - i;
			final byte t = value[i];
			value[i] = value[j];
			value[j] = t;
		}
		return value;
	}

	/***
	 * 得到无符号int
	 * 
	 * @param b
	 *            字节
	 * @return 无符号int
	 */
	public static int toUnsigned(byte b) {
		return b & 0xFF;
	}

	/***
	 * 得到无符号int
	 * 
	 * @param s
	 *            short参数
	 * @return 无符号int
	 */
	public static int toUnsigned(short s) {
		return s & 0xFFFF;
	}

	/***
	 * 得到无符号long
	 * 
	 * @param i
	 *            int参数
	 * @return 无符号int
	 */
	public static long toUnsigned(int i) {
		return i & 0xFFFFFFFFL;
	}

	/***
	 * 读无符号的long
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @param littleEndian
	 *            是否小端字节，true：小端字节 false：大端字节
	 * @return 无符号的long
	 */
	public static long readLong(byte[] bytes, boolean littleEndian) {
		long r = 0;
		for (int i = 0; i < bytes.length; ++i) {
			final long v = toUnsigned(bytes[i]);
			if (littleEndian) {
				r |= (v << (i << 3));
			} else {
				r = (r << 8) | v;
			}
		}
		return r;
	}

	public static long readLongL(byte[] bytes) {
		return readLong(bytes, true);
	}

	public static long readLongL(byte[] bytes, int offset, int length) {
		byte[] temp = Arrays.copyOfRange(bytes, offset, offset + length);
		return readLongL(temp);
	}

	public static long readLongB(byte[] bytes) {
		return readLong(bytes, false);
	}

	/**
	 * 读带符号的Long
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @return 带符号的Long
	 */
	public long readLongSigned(byte[] bytes) {
		long r = 0;
		for (int i = 0; i < bytes.length; ++i) {
			final long v = toUnsigned(bytes[i]);
			r |= (v << (i << 3));
			if ((i == bytes.length - 1) && ((v & 0x80) == 0x80)) {
				for (int j = bytes.length; j < 8; j++) {
					r |= (255 << (j << 3));
				}
			}
		}
		return r;
	}

	/*
	 * public static int byteshas1(byte[] bytes) { int ret = 0; for (int i = 0;
	 * i < bytes.length; i++) { byte b = bytes[i]; for (int j = 0; j < 8; j++) {
	 * if ((b & 0x01 << j) != 0x00) { ret++; } }
	 * 
	 * } return ret; }
	 */

	private static final int ones[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2,
			3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2,
			3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3,
			4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4,
			5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3,
			4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5,
			6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2,
			3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4,
			5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
			6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4,
			5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4,
			5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };

	/***
	 * bytes含有1的个数
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @return 含1的个数
	 */
	public static int byteshas1(byte[] bytes) {
		int ret = 0;
		for (int i = 0; i < bytes.length; i++) {
			ret += ones[bytes[i] & 0xff];
		}
		return ret;
	}

	public static int byteshas1(int a) {
		return (ones[a & 0xff] + ones[(a >> 8) & 0xff] + ones[(a >> 16) & 0xff] + ones[(a >> 24) & 0xff]);
	}

	/***
	 * bytes含有0的个数
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @return 含有0的个数
	 */
	public static int byteshas0(byte[] bytes) {
		int has1 = byteshas1(bytes);
		return bytes.length * 8 - has1;
	}

	/***
	 * 虽然int占4个字节，但有一个符号位，如果要得到无符号的int，bytes最多只能为3，如果大于3字节，请用readLong
	 * 
	 * @param bytes
	 *            要转的字节数组
	 * @param littleEndian
	 *            是否小字节序
	 * @return 得到的int
	 */
	public static int readInt(byte[] bytes, boolean littleEndian) {
		int r = 0;
		for (int i = 0; i < bytes.length; ++i) {
			final int v = toUnsigned(bytes[i]);
			if (littleEndian) {
				r |= (v << (i << 3));
			} else {
				r = (r << 8) | v;
			}
		}
		return r;
	}

	public static int readIntL(byte[] bytes) {
		return readInt(bytes, true);
	}

	public static int readIntL(byte[] bytes, int offset, int length) {
		byte[] temp = Arrays.copyOfRange(bytes, offset, offset + length);
		return readIntL(temp);
	}

	public static int readIntB(byte[] bytes) {
		return readInt(bytes, false);
	}

	/***
	 * 读带符号的整形
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @return 字节中包含的整形
	 */
	public static int readIntSigned(byte[] bytes) {
		int r = 0;
		for (int i = 0; i < bytes.length; ++i) {
			final int v = toUnsigned(bytes[i]);
			r |= (v << (i << 3));
			if ((i == bytes.length - 1) && ((v & 0x80) == 0x80)) {
				for (int j = bytes.length; j < 4; j++) {
					r |= (255 << (j << 3));
				}
			}
		}
		return r;
	}

	/**
	 * 通过字节读字符串
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @return 字节中包含的字符串
	 */
	public static String readString(byte[] bytes) {
		try {
			String retstr = new String(trunc00(bytes), "UTF-8");
			return retstr;
		} catch (UnsupportedEncodingException e) {
			throw new IllegalArgumentException();
		}
	}

	/***
	 * 把前后的00二进制去掉进，要求中间不出现00
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @return 处理后字节
	 */
	public static byte[] trunc00(byte[] bytes) {
		int begindex = -1;
		int endindex = -1;
		for (int i = 0; i < bytes.length; i++) {
			if (bytes[i] != 0x00 && begindex < 0) {
				begindex = i;
				continue;
			}
			if (bytes[i] == 0x00 && begindex >= 0) {
				endindex = i;
				break;
			}
		}

		if (begindex < 0) {
			return ArrayUtils.EMPTY_BYTE_ARRAY;
		} else {
			if (endindex < 0) {
				return bytes;
			} else {
				return ArrayUtils.subarray(bytes, begindex, endindex);
			}
		}
	}

	/****
	 * 读取字节中的字符串
	 * 
	 * @param bytes
	 *            原始字节数组
	 * @param offset
	 *            起始位置
	 * @param length
	 *            处理长度
	 * @return 包含的字符串
	 */
	public static String readString(byte[] bytes, int offset, int length) {
		byte[] temp = Arrays.copyOfRange(bytes, offset, offset + length);
		return readString(trunc00(temp));
	}

	/***
	 * 转为字节数组
	 * 
	 * @param num
	 *            单个字节
	 * @return 字节数组
	 */
	public static byte[] toByteArray(byte num) {
		return new byte[] { num };
	}

	/***
	 * short转为字节数组
	 * 
	 * @param num
	 *            short
	 * @return 字节数组
	 */
	public static byte[] toByteArray(short num) {
		final byte[] r = new byte[2];
		for (int i = 0; i < 2; i++) {
			r[i] = (byte) (num >>> (8 - i * 8));
		}
		return r;
	}

	/***
	 * int转为字节数组
	 * 
	 * @param num
	 *            int
	 * @return 字节数组
	 */
	public static byte[] toByteArray(int num) {
		final byte[] r = new byte[4];
		for (int i = 0; i < 4; i++) {
			r[i] = (byte) (num >>> (24 - i * 8));
		}
		return r;
	}

	/***
	 * long转为字节数组
	 * 
	 * @param num
	 *            long
	 * @return 字节数组
	 */
	public static byte[] toByteArray(long num) {
		final byte[] r = new byte[8];
		for (int i = 0; i < 8; i++) {
			r[i] = (byte) (num >>> (56 - i * 8));
		}
		return r;
	}

	/***
	 * 把data分隔成每行最多maxLength列的多行数组
	 * 
	 * @param data
	 *            要分隔的数据源
	 * @param maxLength
	 *            每行最多列
	 * @return 分隔后的二维数组
	 */
	public static byte[][] splitBytes(byte[] data, int maxLength) {
		if (data.length <= maxLength) {
			return new byte[][] { data };
		} else {
			int length = data.length / maxLength + 1;
			byte[][] retary = new byte[length][maxLength];
			for (int l = 0; l < data.length; l++) {
				int i = l / maxLength;
				int j = l % maxLength;
				retary[i][j] = data[l];
			}
			return retary;

		}
	}

	// ///////////////////////////////
	// 字节数组操作///////////////////////////////////////////////////

	/****
	 * 字节或操作
	 * 
	 * @param data1
	 *            字节1
	 * @param data2
	 *            字节2
	 * @return 取或后的字节
	 */
	public static byte[] or(byte[] data1, byte[] data2) {
		if (data1.length != data2.length) {
			throw new IllegalArgumentException("array lenth does NOT match, "
					+ data1.length + " vs " + data2.length);
		}
		final byte r[] = new byte[data1.length];
		for (int i = 0; i < r.length; i++) {
			r[i] = (byte) (data1[i] | data2[i]);
		}
		return r;
	}

	/****
	 * 字节与操作
	 * 
	 * @param data1
	 *            字节1
	 * @param data2
	 *            字节2
	 * @return 取与后的字节
	 */
	public static byte[] and(byte[] data1, byte[] data2) {
		if (data1.length != data2.length) {
			throw new IllegalArgumentException("array lenth does NOT match, "
					+ data1.length + " vs " + data2.length);
		}
		final byte r[] = new byte[data1.length];
		for (int i = 0; i < r.length; i++) {
			r[i] = (byte) (data1[i] & data2[i]);
		}
		return r;
	}

	/****
	 * 字节异或操作
	 * 
	 * @param data1
	 *            字节1
	 * @param data2
	 *            字节2
	 * @return 取异或后的字节
	 */
	public static byte[] xor(byte[] data1, byte[] data2) {
		if (data1.length != data2.length) {
			throw new IllegalArgumentException("array lenth does NOT match, "
					+ data1.length + " vs " + data2.length);
		}
		final byte r[] = new byte[data1.length];
		for (int i = 0; i < r.length; i++) {
			r[i] = (byte) (data1[i] ^ data2[i]);
		}
		return r;
	}

	/****
	 * 字节判断相等
	 * 
	 * @param data1
	 *            字节1
	 * @param data2
	 *            字节2
	 * @return 判断结果
	 */
	public static boolean equals(byte[] data1, byte[] data2) {
		return Arrays.equals(data1, data2);
	}

	/****
	 * 连接字节数组
	 * 
	 * @param data1
	 *            字节1
	 * @param data2
	 *            字节2
	 * @return 连接后的数组
	 */
	public static byte[] concat(byte[] data1, byte[] data2) {
		final byte r[] = new byte[data1.length + data2.length];
		System.arraycopy(data1, 0, r, 0, data1.length);
		System.arraycopy(data2, 0, r, data1.length, data2.length);
		return r;
	}

	/***
	 * 写帮助类
	 * 
	 * @author andy.zhou
	 *
	 */
	public static class AssitWrite {
		private final byte[] buff;
		private int curPos = 0;

		public AssitWrite(int size) {
			buff = new byte[size];
		}

		/***
		 * 实现小端字节写法,true
		 * 
		 * @param data
		 *            原始字节数组
		 * @param leng
		 *            写入字节长度
		 * @param littleEndian
		 *            是否小端字节，true:小端字节 false:大端字节
		 */
		public void writeByte(byte[] data, int leng, boolean littleEndian) {
			if (littleEndian) {
				org.apache.commons.lang3.ArrayUtils.reverse(data);
			}
			int copylen = data.length > leng ? leng : data.length;
			for (int i = 0; i < copylen; i++) {
				buff[curPos + i] = data[i];
			}
			curPos = curPos + leng;
		}

		public void write(byte[] data) {
			writeByte(data, data.length, false);
		}

		public void write(int data, int leng) {
			writeByte(toByteArray(data), leng, true);
		}

		public void write(long data, int leng) {
			writeByte(toByteArray(data), leng, true);
		}

		public void write(String data, int leng) {
			try {
				writeByte(data.getBytes("UTF-8"), leng, false);
			} catch (UnsupportedEncodingException e) {
			}
		}

		public void write(String data) {
			write(data, data.length());
		}

		public void writeEndNull(byte[] data) {
			write(data);
			write(0, 1);
		}

		public byte[] get() {
			return Arrays.copyOf(buff, curPos);
		}

	}

	/***
	 * 读帮助类
	 * 
	 * @author andy.zhou
	 *
	 */
	public static class AssitRead {
		private final byte[] data;
		private int curpos = 0;

		public int getCurpos() {
			return curpos;
		}

		public AssitRead(byte[] data) {
			this.data = data;
		}

		public byte[] readBytes(int length) {
			byte[] ret = ArrayUtils.subarray(data, curpos, curpos + length);
			curpos += length;
			return ret;
		}

		public byte readByte() {
			return readBytes(1)[0];
		}

		public boolean hasMore() {
			return curpos >= data.length ? false : true;
		}

		public String readStringEndNull() throws IOException {
			byte[] allbyte = new byte[128];// 大多数128就够了
			int i = 0;
			while (true) {
				final byte v = readByte();
				if (v == 0x00)
					break;
				allbyte[i++] = v;
			}
			return ByteUtil.readString(allbyte);
		}

		/***
		 * 首个字节的值小于251用一个byte存储，如果值大于251，fc + 2-byte ，fd + 3-byte integer，fe +
		 * 8-byte
		 * 
		 * @return 读出的UnsignedLong值
		 */
		public UnsignedLong readUnsignedLong() {
			final int v = ByteUtil.toUnsigned(readByte());
			if (v < 251)
				return UnsignedLong.valueOf(v);
			else if (v == 251)
				return null;
			else if (v == 252)
				return UnsignedLong.valueOf(ByteUtil.readLongL(readBytes(2)));
			else if (v == 253)
				return UnsignedLong.valueOf(ByteUtil.readLongL(readBytes(3)));
			else if (v == 254)
				return UnsignedLong.valueOf(ByteUtil.readLongL(readBytes(8)));
			else
				throw new RuntimeException(
						"assertion failed, should NOT reach here");
		}

		public String readStringUnsignedLong() {
			UnsignedLong length = readUnsignedLong();
			String retstr = ByteUtil.readString(readBytes(length.intValue()));
			return retstr;
		}

		/**
		 * 读剩余字节
		 * 
		 * @return 剩余字节
		 */
		public byte[] readRest() {
			byte[] ret = ArrayUtils.subarray(data, curpos, data.length);
			return ret;
		}

		public void skip(int length) {
			curpos += length;
		}
	}

}
